package runtime

import (
	grt "runtime"
	gctx "context"
	"fmt"
	"math"
	"time"
	"bytes"
	"regexp"
	"errors"
	"reflect"
	"strconv"
	"strings"
	"math/big"
	"math/rand"
	"unicode/utf8"
	"kumachan/standalone/qt"
	"kumachan/standalone/ctn"
	"kumachan/standalone/util"
	"kumachan/interpreter/core"
)


var defaultLibraryFunctionList = [] interface{} {
	// Bool
	BoolNo, BoolYes, BoolNot, BoolAnd, BoolOr, BoolEqual, BoolAssert,
	// Int
	IntPlus, IntMinus, IntTimes, IntQuo, IntRem, IntPow,
	IntEqual, IntLessThan, IntCompare,
	// Float
	FloatPlus, FloatMinus, FloatTimes, FloatQuo, FloatRem, FloatPow,
	FloatEqual, FloatLessThan,
	FloatInt, IntFloat, FloatNormal, FloatNaN, FloatInfinite,
	math.Floor, math.Ceil, math.Round,
	math.Sqrt, math.Cbrt, math.Exp, math.Log,
	math.Sin, math.Cos, math.Tan, math.Asin, math.Acos, math.Atan, math.Atan2,
	// Char
	Char,
	CharInt, CharUtf8Size, CharUtf8Bytes,
	CharEqual, CharLessThan, CharCompare,
	// String
	String, StringFromChars,
	StringChars, StringUtf8Size, StringUtf8Bytes, StringShift,
	StringEqual, StringLessThan, StringCompare,
	ListStringJoin, StringSplit, StringCut,
	StringAdvance, StringSatisfy, StringReplace,
	// ToString
	BoolString, OrderingString, IntString, FloatString, CharString,
	// Bytes
	Bytes,
	BytesSize, BytesUtf8String,
	// List
	ListConcat,
	core.Cons, core.Count, ListEmpty, ListFirst,
	ListShift, ListSort, ListWithIndex,
	ListMap, ListDeflateMap, ListFlatMap, ListZipMap, ListFilter, ListScan,
	ListFold,
	// Queue
	Queue,
	QueueEmpty, QueueSize, QueueFirst, QueueList,
	QueueShift, QueueAppend,
	// Heap
	Heap,
	HeapEmpty, HeapSize, HeapFirst, HeapList,
	HeapShift, HeapInsert,
	// Set
	Set,
	SetEmpty, SetSize, SetList,
	SetHas, SetDelete, SetInsert,
	// Map
	Map,
	MapEmpty, MapSize, MapKeys, MapValues, MapEntries,
	MapHas, MapLookup, MapDelete, MapInsert,
	// Observable
	Observable,
	core.WithCancelSignal, core.WithCancelTimeout, core.CompleteOnEmit,
	core.SetTimeout, core.SetInterval,
	Go, Throw, Crash,
	core.Noop, Random, UUID, SeqNum,
	ObservableShare,
	ObservableCatch, ObservableRetry,
	ObservableDistinctUntilChanged,
	ObservableWithLatestFrom,
	ObservableWithCycle, ObservableWithIndex, ObservableWithTime,
	ObservableDelayRun, ObservableDelayValues,
	ObservableStartWith, ObservableEndWith,
	ObservableThrottle, ObservableDebounce,
	ObservableThrottleTime, ObservableDebounceTime,
	ObservableTake, ObservableTakeLast,
	ObservableTakeWhile, ObservableTakeUntil,
	ObservableCount, ObservableCollect,
	ObservableBufferTime,
	ObservablePairwise, ObservableBufferCount,
	ObservableMap, ObservableMapTo, ObservableFilter, ObservableScan,
	ObservableCombineLatest, ListObservableCombineLatest,
	ObservableAwait, ObservableThen, ObservableWith,
	ListObservableMerge, ObservableMerge, ObservableMergeMap,
	ListObservableConcat, ObservableConcat, ObservableConcatMap,
	ObservableSwitchMap, ObservableExhaustMap,
	NumCPU,
	ListObservableConcurrent, ObservableConcurrentMap,
	ListObservableForkJoin, ObservableForkJoin,
	// Time
	TimeString, TimeSubtractMillisecond,
	// None
	None,
	// Error
	Error,
	// misc
	Undefined,
}
var defaultLibrary = (func() *Library {
	var m = make(map[string] core.NativeFunction)
	for _, item := range defaultLibraryFunctionList {
		var rv = reflect.ValueOf(item)
		var name = strings.Split(grt.FuncForPC(rv.Pointer()).Name(), ".")[1]
		var obj = core.MakeNativeFunction(item)
		if _, duplicate := m[name]; duplicate {
			panic("duplicate native function: " + name)
		}
		m[name] = obj
	}
	return &Library { m }
})()


func BoolNo() bool {
	return false
}
func BoolYes() bool {
	return true
}
func BoolNot(p bool) bool {
	return !(p)
}
func BoolAnd(p bool, q bool) bool {
	return (p && q)
}
func BoolOr(p bool, q bool) bool {
	return (p || q)
}
func BoolEqual(p bool, q bool) bool {
	return (p == q)
}
func BoolAssert(ok bool, k func()(core.Object), h core.RuntimeHandle) core.Object {
	if ok {
		return k()
	} else {
		return core.Crash1[core.Object] (
			h, core.AssertionFailed,
			"given boolean value is false",
		)
	}
}

func IntPlus(a *big.Int, b *big.Int) *big.Int {
	var c big.Int
	return c.Add(a, b)
}
func IntMinus(a *big.Int, b *big.Int) *big.Int {
	var c big.Int
	return c.Sub(a, b)
}
func IntTimes(a *big.Int, b *big.Int) *big.Int {
	var c big.Int
	return c.Mul(a, b)
}
func IntQuo(a *big.Int, b *big.Int, h core.RuntimeHandle) *big.Int {
	if b.Sign() == 0 {
		core.Crash(h, core.InvalidArgument, "division by zero")
	}
	var c big.Int
	return c.Quo(a, b)
}
func IntRem(a *big.Int, b *big.Int, h core.RuntimeHandle) *big.Int {
	if b.Sign() == 0 {
		core.Crash(h, core.InvalidArgument, "division by zero")
	}
	var c big.Int
	return c.Rem(a, b)
}
func IntPow(a *big.Int, b *big.Int, h core.RuntimeHandle) *big.Int {
	var c big.Int
	if b.Sign() < 0 {
		core.Crash(h, core.InvalidArgument, "negative integer power")
	}
	return c.Exp(a, b, nil)
}
func IntEqual(a *big.Int, b *big.Int) bool {
	return (a.Cmp(b) == 0)
}
func IntLessThan(a *big.Int, b *big.Int) bool {
	return (a.Cmp(b) < 0)
}
func IntCompare(a *big.Int, b *big.Int) ctn.Ordering {
	var result = a.Cmp(b)
	if result < 0 {
		return ctn.Smaller
	} else if result > 0 {
		return ctn.Bigger
	} else {
		return ctn.Equal
	}
}

func FloatPlus(x float64, y float64) float64 {
	return (x + y)
}
func FloatMinus(x float64, y float64) float64 {
	return (x - y)
}
func FloatTimes(x float64, y float64) float64 {
	return (x * y)
}
func FloatQuo(x float64, y float64) float64 {
	return (x / y)
}
func FloatRem(x float64, y float64) float64 {
	return math.Mod(x, y)
}
func FloatPow(x float64, y float64) float64 {
	return math.Pow(x, y)
}
func FloatEqual(x float64, y float64) bool {
	return (x == y)
}
func FloatLessThan(x float64, y float64) bool {
	return (x < y)
}
func FloatInt(x float64) *big.Int {
	var a, _ = big.NewFloat(x).Int(nil)
	return a
}
func IntFloat(a *big.Int) float64 {
	var x, _ = util.IntegerToDouble(a)
	return x
}
func FloatNormal(x float64) bool {
	return util.IsNormalFloat(x)
}
func FloatNaN(x float64) bool {
	return (x != x)
}
func FloatInfinite(x float64) bool {
	return math.IsInf(x, 0)
}

func Char(n *big.Int, h core.RuntimeHandle) rune {
	if n.IsInt64() {
		n := n.Int64()
		if (0 <= n && n <= 0x10FFFF) && !(0xD800 <= n && n <= 0xDFFF) {
			return rune(n)
		}
	}
	var msg = fmt.Sprintf("invalid codepoint 0x%X", n)
	return core.Crash1[rune](h, core.InvalidArgument, msg)
}
func CharInt(c rune) int {
	return int(c)
}
func CharUtf8Size(c rune) int {
	return utf8.RuneLen(c)
}
func CharUtf8Bytes(c rune) ([] byte) {
	return ([] byte)(string([] rune { c }))
}
func CharEqual(c rune, d rune) bool {
	return (c == d)
}
func CharLessThan(c rune, d rune) bool {
	return (c < d)
}
func CharCompare(c rune, d rune) ctn.Ordering {
	return ctn.DefaultCompare[rune]()(c, d)
}

func String(fragments ([] core.Interface), h core.RuntimeHandle) string {
	var buf strings.Builder
	for _, I := range fragments {
		var object = core.GetSamInterfaceMethodValue(I, h)
		var value = string((*object).(core.String))
		buf.WriteString(value)
	}
	return buf.String()
}
func StringFromChars(chars ([] rune)) string {
	return string(chars)
}
func StringChars(s string) ([] rune) {
	return ([] rune)(s)
}
func StringUtf8Size(s string) int {
	return len(s)
}
func StringUtf8Bytes(s string) ([] byte) {
	return ([] byte)(s)
}
func StringShift(s string) ctn.Optional[ctn.Pair[rune,string]] {
	for _, char := range s {
		var rest = s[utf8.RuneLen(char):]
		return ctn.Just(ctn.MakePair(char, rest))
	}
	return ctn.Null[ctn.Pair[rune,string]]()
}
func StringEqual(a string, b string) bool {
	return (a == b)
}
func StringLessThan(a string, b string) bool {
	return (a < b)
}
func StringCompare(a string, b string) ctn.Ordering {
	return ctn.StringCompare(a, b)
}
func ListStringJoin(l core.List, sep string) string {
	var values = make([] string, 0)
	l.ForEach(func(item core.Object) {
		var value = string((*item).(core.String))
		values = append(values, value)
	})
	return strings.Join(values, sep)
}
func StringSplit(s string, sep string) core.List {
	return core.ToObjectList(strings.Split(s, sep))
}
func StringCut(s string, sep string) ctn.Optional[ctn.Pair[string,string]] {
	if a, b, ok := strings.Cut(s, sep); ok {
		return ctn.Just(ctn.MakePair(a, b))
	} else {
		return ctn.Null[ctn.Pair[string,string]]()
	}
}
func StringAdvance(s string, pattern string, h core.RuntimeHandle) ctn.Optional[ctn.Pair[string,string]] {
	var header_pattern = ("^" + pattern)
	var r = compileRegexpArgument(header_pattern, h)
	if result := r.FindStringIndex(s); (result != nil) {
		if result[0] != 0 { panic("something went wrong") }
		var pos = result[1]
		var match = s[:pos]
		var rest = s[pos:]
		return ctn.Just(ctn.MakePair(match, rest))
	} else {
		return ctn.Null[ctn.Pair[string,string]]()
	}
}
func StringSatisfy(s string, pattern string, h core.RuntimeHandle) bool {
	var full_pattern = ("^" + pattern + "$")
	var r = compileRegexpArgument(full_pattern, h)
	if result := r.FindStringIndex(s); (result != nil) {
		if result[0] != 0 { panic("something went wrong") }
		if result[1] != len(s) { panic("something went wrong") }
		return true
	} else {
		return false
	}
}
func StringReplace(s string, pattern string, f func(string)(string), h core.RuntimeHandle) string {
	var r = compileRegexpArgument(pattern, h)
	return r.ReplaceAllStringFunc(s, f)
}

func BoolString(p bool) string {
	if p {
		return core.TrueString
	} else {
		return core.FalseString
	}
}
func OrderingString(o ctn.Ordering) string {
	return o.String()
}
func IntString(a *big.Int) string {
	return a.String()
}
func FloatString(x float64) string {
	return strconv.FormatFloat(x, 'g', -1, 64)
}
func CharString(c rune) string {
	return string([] rune { c })
}

func Bytes(l core.List) ([] byte) {
	var buf bytes.Buffer
	l.ForEach(func(item core.Object) {
		var fragment = (*item).(core.Bytes)
		buf.Write(fragment)
	})
	return buf.Bytes()
}
func BytesSize(b ([] byte)) int {
	return len(b)
}
func BytesUtf8String(b ([] byte)) string {
	return util.DecodeUtf8(b)
}

func ListConcat(l core.List) core.List {
	var buf core.ListBuilder
	l.ForEach(func(item core.Object) {
		(*item).(core.List).ForEach(func(item core.Object) {
			buf.Append(item)
		})
	})
	return buf.Collect()
}
func ListEmpty(l core.List) bool {
	return l.Empty()
}
func ListFirst(l core.List) ctn.Optional[core.Object] {
	return ctn.MakeOptional(l.First())
}
func ListShift(l core.List) ctn.Optional[ctn.Pair[core.Object,core.List]] {
	var head, tail, ok = l.Shift()
	return ctn.MakeOptional(ctn.MakePair(head, tail), ok)
}
func ListSort(l core.List, I core.Interface, h core.RuntimeHandle) core.List {
	var lt = getLessThanOperator(I, h)
	return l.Sort(lt)
}
func ListWithIndex(l core.List) core.List {
	return l.WithIndex()
}
func ListMap(l core.List, f func(core.Object)(core.Object)) core.List {
	return l.Map(f)
}
func ListDeflateMap(l core.List, f func(core.Object)(ctn.Optional[core.Object])) core.List {
	return l.DeflateMap(f)
}
func ListFlatMap(l core.List, f func(core.Object)(core.List)) core.List {
	return l.FlatMap(f)
}
func ListZipMap(l core.List, m core.List, f func(core.Object,core.Object)(core.Object)) core.List {
	return l.ZipMap(m, f)
}
func ListFilter(l core.List, f func(core.Object)(bool)) core.List {
	return l.Filter(f)
}
func ListScan(l core.List, b core.Object, f func(core.Object,core.Object)(core.Object)) core.List {
	return l.Scan(b, f)
}
func ListFold(l core.List, b core.Object, f func(core.Object,core.Object)(core.Object)) core.Object {
	return l.Fold(b, f)
}

func Queue(items core.List) core.Queue {
	var queue = ctn.MakeQueue[core.Object]()
	items.ForEach(func(item core.Object) {
		queue = queue.Appended(item)
	})
	return core.Queue(queue)
}
func QueueEmpty(q core.Queue) bool {
	var queue = ctn.Queue[core.Object](q)
	return queue.IsEmpty()
}
func QueueSize(q core.Queue) int {
	var queue = ctn.Queue[core.Object](q)
	return queue.Size()
}
func QueueFirst(q core.Queue) ctn.Optional[core.Object] {
	var queue = ctn.Queue[core.Object](q)
	return ctn.MakeOptional(queue.First())
}
func QueueList(q core.Queue) core.List {
	var queue = ctn.Queue[core.Object](q)
	var buf core.ListBuilder
	queue.ForEach(func(item core.Object) {
		buf.Append(item)
	})
	return buf.Collect()
}
func QueueShift(q core.Queue) ctn.Optional[ctn.Pair[core.Object,core.Queue]] {
	var queue = ctn.Queue[core.Object](q)
	var v, rest, ok = queue.Shifted()
	return ctn.MakeOptional(ctn.MakePair(v, core.Queue(rest)), ok)
}
func QueueAppend(q core.Queue, item core.Object) core.Queue {
	var queue = ctn.Queue[core.Object](q)
	return core.Queue(queue.Appended(item))
}

func Heap(items core.List, I core.Interface, h core.RuntimeHandle) core.Heap {
	var lt = getLessThanOperator(I, h)
	var heap = ctn.MakeHeap(lt)
	items.ForEach(func(item core.Object) {
		heap = heap.Inserted(item)
	})
	return core.Heap(heap)
}
func HeapEmpty(h core.Heap) bool {
	var heap = ctn.Heap[core.Object](h)
	return heap.IsEmpty()
}
func HeapSize(h core.Heap) int {
	var heap = ctn.Heap[core.Object](h)
	return heap.Size()
}
func HeapFirst(h core.Heap) ctn.Optional[core.Object] {
	var heap = ctn.Heap[core.Object](h)
	return ctn.MakeOptional(heap.First())
}
func HeapList(h core.Heap) core.List {
	var heap = ctn.Heap[core.Object](h)
	var buf core.ListBuilder
	heap.ForEach(func(item core.Object) {
		buf.Append(item)
	})
	return buf.Collect()
}
func HeapShift(h core.Heap) ctn.Optional[ctn.Pair[core.Object,core.Heap]] {
	var heap = ctn.Heap[core.Object](h)
	var v, rest, ok = heap.Shifted()
	return ctn.MakeOptional(ctn.MakePair(v, core.Heap(rest)), ok)
}
func HeapInsert(h core.Heap, item core.Object) core.Heap {
	var heap = ctn.Heap[core.Object](h)
	return core.Heap(heap.Inserted(item))
}

func Set(items core.List, I core.Interface, h core.RuntimeHandle) core.Set {
	var cmp = getCompare(I, h)
	var set = ctn.MakeSet[core.Object](cmp)
	items.ForEach(func(item core.Object) {
		set = set.Inserted(item)
	})
	return core.Set(set)
}
func SetEmpty(s core.Set) bool {
	var set = ctn.Set[core.Object](s)
	return set.IsEmpty()
}
func SetSize(s core.Set) int {
	var set = ctn.Set[core.Object](s)
	return set.Size()
}
func SetList(s core.Set) core.List {
	var set = ctn.Set[core.Object](s)
	var buf core.ListBuilder
	set.ForEach(func(item core.Object) {
		buf.Append(item)
	})
	return buf.Collect()
}
func SetHas(s core.Set, item core.Object) bool {
	var set = ctn.Set[core.Object](s)
	return set.Has(item)
}
func SetDelete(s core.Set, item core.Object) core.Set {
	var set = ctn.Set[core.Object](s)
	var new_set, _ = set.Deleted(item)
	return core.Set(new_set)
}
func SetInsert(s core.Set, item core.Object) core.Set {
	var set = ctn.Set[core.Object](s)
	return core.Set(set.Inserted(item))
}

func Map(entries core.List, I core.Interface, h core.RuntimeHandle) core.Map {
	var cmp = getCompare(I, h)
	var map_ = ctn.MakeMap[core.Object,core.Object](cmp)
	entries.ForEach(func(entry core.Object) {
		var pair = core.FromObject[ctn.Pair[core.Object,core.Object]](entry)
		map_ = map_.Inserted(pair.Key(), pair.Value())
	})
	return core.Map(map_)
}
func MapEmpty(m core.Map) bool {
	var map_ = ctn.Map[core.Object,core.Object](m)
	return map_.IsEmpty()
}
func MapSize(m core.Map) int {
	var map_ = ctn.Map[core.Object,core.Object](m)
	return map_.Size()
}
func MapKeys(m core.Map) core.List {
	var map_ = ctn.Map[core.Object,core.Object](m)
	var buf core.ListBuilder
	map_.ForEach(func(k core.Object, _ core.Object) {
		buf.Append(k)
	})
	return buf.Collect()
}
func MapValues(m core.Map) core.List {
	var map_ = ctn.Map[core.Object,core.Object](m)
	var buf core.ListBuilder
	map_.ForEach(func(_ core.Object, v core.Object) {
		buf.Append(v)
	})
	return buf.Collect()
}
func MapEntries(m core.Map) core.List {
	var map_ = ctn.Map[core.Object,core.Object](m)
	var buf core.ListBuilder
	map_.ForEach(func(k core.Object, v core.Object) {
		buf.Append(core.ToObject(ctn.MakePair(k, v)))
	})
	return buf.Collect()
}
func MapHas(m core.Map, key core.Object) bool {
	var map_ = ctn.Map[core.Object,core.Object](m)
	return map_.Has(key)
}
func MapLookup(m core.Map, key core.Object) ctn.Optional[core.Object] {
	var map_ = ctn.Map[core.Object,core.Object](m)
	return ctn.MakeOptional(map_.Lookup(key))
}
func MapDelete(m core.Map, key core.Object) core.Map {
	var map_ = ctn.Map[core.Object,core.Object](m)
	var _, new_map, _ = map_.Deleted(key)
	return core.Map(new_map)
}
func MapInsert(m core.Map, entry ctn.Pair[core.Object,core.Object]) core.Map {
	var map_ = ctn.Map[core.Object,core.Object](m)
	return core.Map(map_.Inserted(entry.Key(), entry.Value()))
}

func Observable(l core.List) core.Observable {
	return core.Observable(func(pub core.DataPublisher) {
		pub.DoSyncWithYield(func(yield func(core.Object)) error {
			l.ForEach(yield)
			return nil
		})
	})
}
func Go(k func()(core.Object)) core.Observable {
	return core.Observable(func(pub core.DataPublisher) {
		pub.Go(func(_ gctx.Context) (core.Object, error) {
			return k(), nil
		})
	})
}
func Throw(e error) core.Observable {
	return core.Observable(func(pub core.DataPublisher) {
		pub.DoSync(func() (core.Object, error) {
			return nil, e
		})
	})
}
func Crash(err error, h core.RuntimeHandle) core.Observable {
	return core.Observable(func(pub core.DataPublisher) {
		pub.DoSync(func() (core.Object, error) {
			return core.Crash2[core.Object,error](
				h, core.RunOfCrashObservable, err.Error(),
			)
		})
	})
}
func Random() core.Observable {
	return core.Observable(func(pub core.DataPublisher) {
		pub.DoSync(func() (core.Object, error) {
			var x = rand.Float64()
			return core.ObjFloat(x), nil
		})
	})
}
func UUID() core.Observable {
	return core.Observable(func(pub core.DataPublisher) {
		pub.DoSync(func() (core.Object, error) {
			var uuid = qt.UUID()
			return core.ObjString(uuid), nil
		})
	})
}
var SeqNumCurrentValue = uint64(0)
func SeqNum() core.Observable {
	return core.Observable(func(pub core.DataPublisher) {
		pub.DoSync(func() (core.Object, error) {
			var n = SeqNumCurrentValue
			SeqNumCurrentValue++
			return core.ObjIntFromInt64(int64(n)), nil
		})
	})
}
func ObservableShare(o core.Observable) core.Observable {
	return core.Multicast(o)
}
func ObservableCatch(o core.Observable, f func(error,core.Observable)(core.Observable)) core.Observable {
	return o.Catch(f)
}
func ObservableRetry(o core.Observable, n int) core.Observable {
	return o.Retry(n)
}
func ObservableDistinctUntilChanged(o core.Observable, I core.Interface, h core.RuntimeHandle) core.Observable {
	var eq = getEqualOperator(I, h)
	return o.DistinctUntilChanged(eq)
}
func ObservableWithLatestFrom(o core.Observable, another core.Observable) core.Observable {
	return o.WithLatestFrom(another)
}
func ObservableWithCycle(o core.Observable, l core.List) core.Observable {
	return o.WithCycle(l)
}
func ObservableWithIndex(o core.Observable) core.Observable {
	return o.WithIndex()
}
func ObservableWithTime(o core.Observable) core.Observable {
	return o.WithTime()
}
func ObservableDelayRun(o core.Observable, ms int) core.Observable {
	return o.DelayRun(ms)
}
func ObservableDelayValues(o core.Observable, ms int) core.Observable {
	return o.DelayValues(ms)
}
func ObservableStartWith(o core.Observable, first core.Object) core.Observable {
	return o.StartWith(first)
}
func ObservableEndWith(o core.Observable, last core.Object) core.Observable {
	return o.EndWith(last)
}
func ObservableThrottle(o core.Observable, f func(core.Object)(core.Observable)) core.Observable {
	return o.Throttle(f)
}
func ObservableDebounce(o core.Observable, f func(core.Object)(core.Observable)) core.Observable {
	return o.Debounce(f)
}
func ObservableThrottleTime(o core.Observable, ms int) core.Observable {
	return o.ThrottleTime(ms)
}
func ObservableDebounceTime(o core.Observable, ms int) core.Observable {
	return o.DebounceTime(ms)
}
func ObservableTake(o core.Observable, limit int) core.Observable {
	return o.Take(limit)
}
func ObservableTakeLast(o core.Observable) core.Observable {
	return o.TakeLast()
}
func ObservableTakeWhile(o core.Observable, f func(core.Object)(bool)) core.Observable {
	return o.TakeWhile(f)
}
func ObservableTakeUntil(o core.Observable, stop core.Observable) core.Observable {
	return o.TakeUntil(stop)
}
func ObservableCount(o core.Observable) core.Observable {
	return o.Count()
}
func ObservableCollect(o core.Observable) core.Observable {
	return o.Collect()
}
func ObservableBufferTime(o core.Observable, ms int) core.Observable {
	return o.BufferTime(ms)
}
func ObservablePairwise(o core.Observable) core.Observable {
	return o.Pairwise()
}
func ObservableBufferCount(o core.Observable, n int) core.Observable {
	return o.BufferCount(n)
}
func ObservableMap(o core.Observable, f func(core.Object)(core.Object)) core.Observable {
	return o.Map(f)
}
func ObservableMapTo(o core.Observable, v core.Object) core.Observable {
	return o.MapTo(v)
}
func ObservableFilter(o core.Observable, f func(core.Object)(bool)) core.Observable {
	return o.Filter(f)
}
func ObservableScan(o core.Observable, init core.Object, f func(core.Object,core.Object)(core.Object)) core.Observable {
	return o.Scan(init, f)
}
func ObservableCombineLatest(o core.Observable, another core.Observable) core.Observable {
	return o.CombineLatest(another)
}
func ListObservableCombineLatest(l ([] core.Observable)) core.Observable {
	return core.CombineLatest(l...)
}
func ObservableAwait(o core.Observable, k func(core.Object)(core.Observable)) core.Observable {
	return o.Await(k)
}
func ObservableThen(o core.Observable, k core.Observable) core.Observable {
	return o.Then(k)
}
func ObservableWith(o core.Observable, bg core.Observable) core.Observable {
	return o.With(bg)
}
func ListObservableMerge(l core.List) core.Observable {
	return core.Merge(forEachObservable(l))
}
func ObservableMerge(o1 core.Observable, o2 core.Observable) core.Observable {
	return core.Merge(core.YieldObservables(o1, o2))
}
func ObservableMergeMap(o core.Observable, f func(core.Object)(core.Observable)) core.Observable {
	return o.MergeMap(f)
}
func ListObservableConcat(l core.List) core.Observable {
	return core.Concat(forEachObservable(l))
}
func ObservableConcat(o1 core.Observable, o2 core.Observable) core.Observable {
	return core.Concat(core.YieldObservables(o1, o2))
}
func ObservableConcatMap(o core.Observable, f func(core.Object)(core.Observable)) core.Observable {
	return o.ConcatMap(f)
}
func ObservableSwitchMap(o core.Observable, f func(core.Object)(core.Observable)) core.Observable {
	return o.SwitchMap(f)
}
func ObservableExhaustMap(o core.Observable, f func(core.Object)(core.Observable)) core.Observable {
	return o.ExhaustMap(f)
}
func NumCPU() int {
	return grt.NumCPU()
}
func ListObservableConcurrent(l core.List, n int) core.Observable {
	return core.Concurrent(n, forEachObservable(l))
}
func ObservableConcurrentMap(o core.Observable, f func(core.Object)(core.Observable), n int) core.Observable {
	return o.ConcurrentMap(n, f)
}
func ListObservableForkJoin(l ([] core.Observable), n int) core.Observable {
	return core.ForkJoin(n, l...)
}
func ObservableForkJoin(o core.Observable, another core.Observable, n int) core.Observable {
	return o.ForkJoin(n, another)
}

func TimeString(t time.Time) string {
	return t.String()
}
func TimeSubtractMillisecond(t time.Time, u time.Time) int {
	return int(t.Sub(u).Milliseconds())
}

func None() core.Object {
	return nil
}

func Error(msg string) error {
	return errors.New(msg)
}

func Undefined(msg string, h core.RuntimeHandle) core.Object {
	return core.Crash1[core.Object] (
		h, core.Undefined,
		strconv.Quote(msg),
	)
}


func getCompare(I core.Interface, h core.RuntimeHandle) ctn.Compare[core.Object] {
	var obj = core.GetSamInterfaceMethodValue(I, h)
	return core.FromObject[func(core.Object,core.Object)(ctn.Ordering)](obj)
}
func getEqualOperator(I core.Interface, h core.RuntimeHandle) func(core.Object,core.Object)(bool) {
	var obj = core.GetSamInterfaceMethodValue(I, h)
	return core.FromObject[func(core.Object,core.Object)(bool)](obj)
}
func getLessThanOperator(I core.Interface, h core.RuntimeHandle) ctn.LessThanOperator[core.Object] {
	var obj = core.GetSamInterfaceMethodValue(I, h)
	return core.FromObject[func(core.Object,core.Object)(bool)](obj)
}
func compileRegexpArgument(pattern string, h core.RuntimeHandle) *regexp.Regexp {
	var value, err = regexp.Compile(pattern)
	if err != nil {
		var quote = strconv.Quote(pattern)
		var detail = err.Error()
		var msg = fmt.Sprintf("invalid regexp %s: %s", quote, detail)
		core.Crash(h, core.InvalidArgument, msg)
	}
	return value
}
func forEachObservable(l core.List) func(func(core.Observable)) {
	return func(yield func(core.Observable)) {
		l.ForEach(func(item core.Object) {
			yield(core.GetObservable(item))
		})
	}
}


